package nebula

import (
	"errors"
	"fmt"
	"net"
	"reflect"
	"sync"
	"time"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
)

type DisconnectSocketHandler func(reason string)

var (
	AckError    = errors.New("Bad Ack")
	socketPool  = sync.Pool{}
	requestPool = sync.Pool{}
)

func init() {
	socketPool.New = func() interface{} {
		s := &Socket{}
		s.init()
		return s
	}
	requestPool.New = func() interface{} {
		r := &Request{}
		return r
	}
}

type Request struct {
	*Socket
	async    bool
	rid      int
	compress bool
	data     []interface{}
	err      error
}

func (request *Request) SetCompress(compress bool) {
	request.compress = compress
}

func (request *Request) Response(args ...interface{}) {
	if len(args) > 0 {
		tt, ok := args[0].(error)
		if ok {
			request.ResponseErr(tt)
			return
		}
	}
	request.data = args
	if request.async {
		request.async = false
		request.response()
	}
}

func (request *Request) Async() {
	request.async = true
}

func (request *Request) Finish() {
	request.async = false
	requestPool.Put(request)
}

func (request *Request) Bind(req *Request) {
	request.compress = req.compress
	request.data = req.data
	request.err = req.err
	request.async = req.async
	requestPool.Put(req)
}

func (request *Request) Namespace(namespace Namespace) {
	socket, err := request.Socket.SwitchNamespace(namespace)
	if err != nil {
		return
	}
	request.Socket = socket
}

func (request *Request) ResponseErr(err error) {
	request.err = err
	if request.async {
		request.async = false
		request.response()
	}
}

func (request *Request) response() {
	//fmt.Println("response", request.async, request.err, request.rid)
	if request.async {
		return
	}
	if request.err != nil {
		request.Socket.sendErrAck(request.rid, request.err)
	} else if request.rid > 0 {
		request.Socket.sendAck(request.rid, request.data, request.compress)
	}
	requestPool.Put(request)
}

type Socket struct {
	def.Logger
	def.Store

	client     *clientInline
	Namespace  *serverNamespace
	ExitStatus ExitStatus
	fns        []Middleware
	components def.ComponentSlice
	connected  bool

	acksmu sync.Mutex
	acks   map[int]*utils.Caller
	ids    int

	query map[string]string

	disconnectHandler DisconnectSocketHandler
	event             utils.Event
	Nsp               NSP
}

func newSocket(namespace *serverNamespace, client *clientInline) *Socket {
	s := socketPool.Get().(*Socket)
	s.client = client
	s.Logger = client.Logger
	s.Store = client.Store
	s.Namespace = namespace
	s.ExitStatus = NoneExit
	s.connected = true
	s.ids = 0
	s.Nsp = namespace.nsp
	return s
}

func (n *Socket) init() {

	n.fns = []Middleware{}
	n.components = []def.Component{}
	n.acks = make(map[int]*utils.Caller, 3)
	n.event = utils.NewEvent()
}

func (n *Socket) Release() {
	n.disconnectHandler = nil
	n.ids = 0
	n.query = nil
	n.event = nil
	n.client = nil
	n.Namespace = nil

	n.init()
	socketPool.Put(n)
}

func (s *Socket) ID() string {
	return s.client.ID()
}

func (s *Socket) Close() {
	if !s.connected {
		return
	}
	s.ExitStatus = ServerExit
	s.client.Close()
}

func (s *Socket) Disconnect() {
	if !s.connected {
		return
	}
	s.ExitStatus = ServerExit

	s.packet(&NsData{Type: N_DISCONNECT}, false)
	s.destroy("server namespace disconnect")
}

func (s *Socket) SendError(err error) {
	s.sendErrAck(0, err)
}

func (s *Socket) On(event NSE, handler interface{}) error {
	return s.event.On(event, handler)
}

func (s *Socket) Trigger(event NSE, params []interface{}) (*Request, error) {
	var e error
	req := requestPool.Get().(*Request)
	req.async = false
	req.Socket = s
	req.rid = 0
	req.data = nil
	req.err = nil
	req.compress = s.Namespace.compress
	if s.event.HasEvent(event) {
		e = s.fire(event, req, params)
	} else {
		e = s.Namespace.fire(event, req, params)
	}
	return req, e
}

func (s *Socket) SwitchNamespace(namespace Namespace) (*Socket, error) {
	return s.client.With(namespace.Path())
}

func (s *Socket) Disconnected(handler DisconnectSocketHandler) error {
	s.disconnectHandler = handler
	return nil
}

func (s *Socket) Emit(event NSE, args ...interface{}) error {
	return s.emit(event, s.Namespace.compress, args)
}

func (s *Socket) EmitCompress(event NSE, args ...interface{}) error {
	return s.emit(event, true, args)
}

func (s *Socket) emit(event NSE, compress bool, args []interface{}) error {
	packet := &NsData{
		Type: N_EVENT,
	}
	var c *utils.Caller
	if l := len(args); l > 0 {
		lastP := args[l-1]
		fv := reflect.ValueOf(lastP)
		if fv.Kind() == reflect.Func {
			var err error
			c, err = utils.NewCaller(lastP)
			if err != nil {
				return err
			}
			args = args[:l-1]
			s.acksmu.Lock()
			s.acks[s.ids] = c
			packet.Id = s.ids + 1
			s.acksmu.Unlock()
		}
	}
	packet.Data = append([]interface{}{event}, args...)
	s.packet(packet, compress)
	//fmt.Println(event, compress)
	return nil
}

func (s *Socket) App() *AppPacket {
	return s.client.App()
}

func (s *Socket) Add(component def.Component) error {
	index := s.components.Search(component)
	if index >= 0 {
		return fmt.Errorf("Component %#v is exits", component)
	}
	err := component.Join(s)
	if err != nil {
		return err
	}
	s.components = s.components.Append(component)
	return nil
}

func (s *Socket) Delete(component def.Component) error {
	id := component.ID()
	index := s.components.Search(component)
	if index == -1 {
		return fmt.Errorf("Component %s is not exits", id)
	}
	err := component.Remove(s)
	if err != nil {
		return err
	}
	s.components = s.components.Remove(index)
	return nil
}

func (s *Socket) Group(category string) []def.Component {
	c := make([]def.Component, 0)
	for _, component := range s.components {
		if component.Group(category) {
			c = append(c, component)
		}
	}
	return c
}

func (s *Socket) Use(middleware Middleware) *Socket {
	s.fns = append(s.fns, middleware)
	return s
}

func (s *Socket) Query(key string) (string, bool) {
	value, ok := s.query[key]
	return value, ok
}

func (s *Socket) With(nsp NSP) (*Socket, error) {
	return s.client.With(nsp)
}

func (s *Socket) Delay() (*time.Duration, *time.Time) {
	return s.client.pp.Delay()
}

func (s *Socket) run(next Next) {
	l := len(s.fns)
	if l == 0 {
		_ = next(nil)
		return
	}
	_ = s.m(l, 0, next)
}

func (s *Socket) m(all int, index int, next Next) error {
	return s.fns[index](s, func(err error) error {
		if err != nil {
			return next(err)
		} else if index+1 >= all {
			return next(nil)
		} else {
			return s.m(all, index+1, next)
		}
	})
}

func (s *Socket) dispatch(event interface{}, id int, args []interface{}) {
	s.run(func(err error) error {
		if err != nil {
			return err
		}
		var e error
		req := requestPool.Get().(*Request)
		req.async = false
		req.Socket = s
		req.rid = id
		req.data = nil
		req.err = nil
		req.compress = s.Namespace.compress
		//fmt.Println("socket", s.Namespace.ns, event)
		if s.event.HasEvent(event) {
			e = s.fire(event, req, args)
		} else {
			e = s.Namespace.fire(event, req, args)
		}
		if e != nil {
			s.sendErrAck(id, e)
		} else {
			req.response()
		}
		//fmt.Println(event, req.compress)
		return nil
	})
}

func (s *Socket) onEvent(packet *NsData) {
	var args = packet.Data.([]interface{})
	e := args[0]
	args = args[1:]

	//s.Debugf("client:accept event %s", NSE(utils.ToUint8(e)))
	s.dispatch(NSE(utils.ToUint8(e)), packet.Id, args)
}

func (s *Socket) onDisconnect() {
	if !s.connected {
		return
	}
	s.ExitStatus = ClientExit
	s.destroy("client namespace disconnect")
}

func (s *Socket) onClose() {
	if !s.connected {
		return
	}
	s.ExitStatus = ClientExit
	s.destroy("client close")
}

func (s *Socket) destroy(reason string) {
	if !s.connected {
		return
	}
	s.connected = false
	if s.disconnectHandler != nil {
		s.disconnectHandler(reason)
	}
	s.Namespace.disconnect(s, reason)
	for _, component := range s.components {
		_ = component.Remove(s)
	}
	_ = s.client.Disconnect(s)
	s.Release()
}

func (s *Socket) onError(packet *NsData) {
	err := errors.New(packet.Data.(string))
	if packet.Id > 0 {
		ack, ok := s.acks[packet.Id]
		if !ok {
			s.SendError(AckError)
			return
		}
		ack.Call(nil)
	}
	s.Error("Remote Error:", err)
}

func (s *Socket) onAck(packet *NsData) {
	ack, ok := s.acks[packet.Id]
	if !ok {
		s.SendError(AckError)
		return
	}
	fv := reflect.ValueOf(ack)
	if fv.Kind() != reflect.Func {

		s.SendError(AckError)
		return
	}
	param := append([]interface{}{s}, packet.Data.([]interface{})...)
	ack.Call(param)
	delete(s.acks, packet.Id)
}

func (s *Socket) sendAck(id int, args []interface{}, compress bool) {
	s.packet(&NsData{
		Id:   id,
		Type: N_ACK,
		Data: args,
	}, compress)
}

func (s *Socket) sendErrAck(id int, err error) {
	exception, ok := err.(def.Exception)
	message := err.Error()
	if ok {
		message = exception.Message()
	}
	//debug.PrintStack()
	s.client.Error("Server Error:", err, message)
	s.packet(&NsData{Id: id, Type: N_ERROR, Data: message}, false)
}

func (s *Socket) onConnect() {
	s.connected = true
	s.packet(&NsData{Type: N_CONNECT}, false)
}

func (s *Socket) fire(event interface{}, req *Request, params []interface{}) error {
	value, err := s.event.FireInterface(event, params)
	if err != nil {
		return err
	}
	req.data = value
	return nil
}

func (s *Socket) packet(packet *NsData, compress bool) {
	if packet.NSP == nil || len(packet.NSP) == 0 {
		packet.NSP = s.Nsp
	}
	s.client.Packet(packet, s.Namespace, compress)
}

func (s *Socket) RemoteAddr() net.Addr {
	return s.client.conn.RemoteAddr()
}
